/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
 * ----------------------------------------------------------------------------
 *
 * HD44780 LCD display driver
 *
 * The LCD controller is used in 4-bit mode with a full bi-directional
 * interface (i.e. R/~W is connected) so the busy flag can be read.
 *
 * $Id: hd44780.c,v 1.1.2.3 2006/10/08 21:51:14 joerg_wunsch Exp $
 */

//#include "defines.h"

#include <FreeRTOS.h>
#include <task.h>

#include <stdint.h>
#include <stm32f10x.h>

#include "platform_config.h"

#include "hd44780.h"

//#define bool	_Bool
#define true	1
#define false	0
//
#define HD44780_BUSYFLAG 0x80
//

static void _delay_ms( int ms )
{
int i;
	for( ; ms > 0; ms-- )
		for( i = 20000; i > 0; i-- )
			asm volatile ("nop");
}


/*
 * Send one pulse to the E signal (enable).  Mind the timing
 * constraints.  If readback is set to true, read the HD44780 data
 * pins right before the falling edge of E, and return that value.
 */

static inline uint8_t
hd44780_pulse_e(bool readback) __attribute__((always_inline));

static inline uint8_t hd44780_pulse_e( bool readback )
{
uint8_t xx;

	GPIO_SetBits( LCD_PORT, LCD_E_PIN );
	//PIO_Set( &pin_hd_E );
  /*
   * Guarantee at least 500 ns of pulse width.  For high CPU
   * frequencies, a delay loop is used.  For lower frequencies, NOPs
   * are used, and at or below 1 MHz, the native pulse width will
   * already be 1 us or more so no additional delays are needed.
   */
	if( readback )
		_delay_ms( 10 );

	//vTaskDelay(1);
	if( readback )
	{
		xx 	= ( GPIO_ReadInputDataBit( LCD_PORT, LCD_D0_PIN ) << 0 );
		xx |= ( GPIO_ReadInputDataBit( LCD_PORT, LCD_D1_PIN ) << 1 );
		xx |= ( GPIO_ReadInputDataBit( LCD_PORT, LCD_D2_PIN ) << 2 );
		xx |= ( GPIO_ReadInputDataBit( LCD_PORT, LCD_D3_PIN ) << 3 );
	}
	else
		xx = 0;

	GPIO_ResetBits( LCD_PORT, LCD_E_PIN );
	return xx;
}

void hd44780_outnibble(uint8_t n, uint8_t rs)
{
	if( n & 0x08 )
		GPIO_SetBits( LCD_PORT, LCD_D3_PIN );
	else
		GPIO_ResetBits( LCD_PORT, LCD_D3_PIN );

	if( n & 0x04 )
		GPIO_SetBits( LCD_PORT, LCD_D2_PIN );
	else
		GPIO_ResetBits( LCD_PORT, LCD_D2_PIN );

	if( n & 0x02 )
		GPIO_SetBits( LCD_PORT, LCD_D1_PIN );
	else
		GPIO_ResetBits( LCD_PORT, LCD_D1_PIN );

	if( n & 0x01 )
		GPIO_SetBits( LCD_PORT, LCD_D0_PIN );
	else
		GPIO_ResetBits( LCD_PORT, LCD_D0_PIN );

	(void)hd44780_pulse_e( false );
}


/*
 * Send one byte to the LCD controller.  As we are in 4-bit mode, we
 * have to send two nibbles.
 */
void hd44780_outbyte( uint8_t n, uint8_t rs )
{
	GPIO_ResetBits( LCD_PORT, LCD_RW_PIN );

	if( rs )
		GPIO_SetBits( LCD_PORT, LCD_A0_PIN );
	else
		GPIO_ResetBits( LCD_PORT, LCD_A0_PIN );

	hd44780_outnibble(n >> 4, rs);
	hd44780_outnibble(n & 0x0F, rs);
}


/*
 * Read one byte (i.e. two nibbles) from the LCD controller.
 */
uint8_t hd44780_inbyte( uint8_t rs )
{
uint8_t x;

	GPIO_SetBits( LCD_PORT, LCD_RW_PIN );
	if( rs )
		GPIO_SetBits( LCD_PORT, LCD_A0_PIN );
	else
		GPIO_ResetBits( LCD_PORT, LCD_A0_PIN );

	x = hd44780_pulse_e(true) << 3;
	x |= hd44780_pulse_e(true);
	GPIO_ResetBits( LCD_PORT, LCD_RW_PIN );
	return x;
}

/*
 * Wait until the busy flag is cleared.
 */
void hd44780_wait_ready( void )
{
  while( hd44780_incmd() & HD44780_BUSYFLAG );
}

/*
 * Initialize the LCD controller.
 *
 * The initialization sequence has a mandatory timing so the
 * controller can safely recognize the type of interface desired.
 * This is the only area where timed waits are really needed as
 * the busy flag cannot be probed initially.
 */
void hd44780_init( void )
{
	// Начальная инициализация HD44780
	hd44780_outnibble( 0x03, false );
	_delay_ms( 50 );

	hd44780_outnibble( 0x03, false );
	_delay_ms( 50 );

	hd44780_outnibble( 0x03, false );
	_delay_ms( 50 );

	hd44780_outnibble( 0x02, false );
	_delay_ms( 50 );

	// 4 бита, 1я страница знакогенератора
	hd44780_outbyte( 42, 0 );
	_delay_ms(50);

	// Выключим, курсор невидимый
	hd44780_outbyte( 15, 0 );
	_delay_ms(50);

	// Очистить, курсор в 1е положение
	hd44780_outbyte( 1, 0 );
	_delay_ms(50);

	// Курсор сдвигаем вправо
	hd44780_outbyte( 6, 0 );
	_delay_ms(50);
}

